package tue.cse.advanceddatabases.project;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;

import org.dom4j.Element;

public class RatingsManagerBean extends MusicEngineBean {
	
	private static final String RATING_TYPE_ALBUM = "ALBUM";
	private static final String RATING_TYPE_ARTIST = "ARTIST";
	private static final String RATING_TYPE_TRACK = "TRACK";
	
	private Logger logger = Logger.getLogger(RatingsManagerBean.class.getName());
	
	LoginManager loginManagerBean;
	
	public LoginManager getLoginManagerBean() {
		return loginManagerBean;
	}
	public void setLoginManagerBean(LoginManager loginManagerBean) {
		this.loginManagerBean = loginManagerBean;
	}

	/*
	 * public methods (actions)
	 */
	
	public void insertAlbumRating(){
		
		logger.info("Inserting Album Rating");
		insertRating(RATING_TYPE_ALBUM);
		updateAlbumRating();
	}
	
	public void insertTrackRating(){
		
		logger.info("Inserting Track Rating");
		insertRating(RATING_TYPE_TRACK);
		updateTrackRating();
	}
	
	public void insertArtistRating(){
		
		logger.info("Inserting Artist Rating");
		insertRating(RATING_TYPE_ARTIST);
		updateArtistRating();
	}
	
	
	private void insertRating(String aRatingType) {
		logger.info("Inserting Rating");
		
		if (getRating() == null || getRating().trim().length() == 0){
			logger.info("Rating Text is empty.");	
			return;
		}
				  
		String ratingType = aRatingType;
		String musicId = getMusicId();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String date = sdf.format(new Date());
		
		//TODO: generate  commentId 
		String ratingId = date;
		
		String command = getInsertRatingCommand(ratingId, ratingType, musicId, date, getRating(), 
				loginManagerBean.getLoginId());
		
		String query = updateBegin() + command + updateEnd();
		String result = executeQuery(query,"Ratings.xml");
		
		logger.info("Inserting result is:" + result);
		
		//setText(null);
	}
	
	/** update album rating **/
	private void updateAlbumRating() {
		String albumId = getMusicId();
		String rating = getRating();

		// Update Rating value
		String command = getUpdateAlbumRatingQuery(albumId, rating);
		String query = queryBegin() + command + queryEnd();
		String result = executeQuery(query,"Albums.xml");	
		logger.info("updateAlbumRating result is:" + result);
		
		// Update Rating Count
		command = getUpdateAlbumRatingCountQuery(albumId);
		query = queryBegin() + command + queryEnd();
		result = executeQuery(query,"Albums.xml");	
		logger.info("updateAlbumRating result is:" + result);
	}
	
	/** update album rating **/
	private void updateArtistRating() {
		String albumId = getMusicId();
		String rating = getRating();

		// Update Rating value
		String command = getUpdateArtistRatingQuery(albumId, rating);
		String query = queryBegin() + command + queryEnd();
		String result = executeQuery(query,"Artists.xml");	
		logger.info("updateArtistRating result is:" + result);
		
		// Update Rating Count
		command = getUpdateArtistRatingCountQuery(albumId);
		query = queryBegin() + command + queryEnd();
		result = executeQuery(query,"Artists.xml");	
		logger.info("updateArtistRating result is:" + result);
	}
	
	/** update album rating **/
	private void updateTrackRating() {
		String trackId = getMusicId();
		String rating = getRating();

		// Update Rating value
		String command = getUpdateTrackRatingQuery(trackId, rating);
		String query = queryBegin() + command + queryEnd();
		String result = executeQuery(query,"Tracks.xml");	
		logger.info("updateTrackRating result is:" + result);
		
		// Update Rating Count
		command = getUpdateTrackRatingCountQuery(trackId);
		query = queryBegin() + command + queryEnd();
		result = executeQuery(query,"Tracks.xml");	
		logger.info("updateTrackRating result is:" + result);
	}
	
	private String getRating() {
		FacesContext facesContext = FacesContext.getCurrentInstance();
		return (String) facesContext.getExternalContext().getRequestParameterMap().get("rating");
	}
	
	/** get the comment music id of the request parameters **/
	private String getMusicId() {
		FacesContext facesContext = FacesContext.getCurrentInstance();
		return (String) facesContext.getExternalContext().getRequestParameterMap().get("ratingMusicId");
	}
	

	
	/** return true if "albumKey" is already rated by the current logged user **/
	public boolean getIsAlbumRated(){
		
		String musicId = getAlbumKey();
		String ratingResult = searchRating(RATING_TYPE_ALBUM, musicId);
		
		if (ratingResult != null){
			List<Element> ratingElement = removeExistDBElement(ratingResult);
			if (ratingElement!=null){
				if (ratingElement.get(0).elements()!=null && ratingElement.get(0).elements().size()>0)
					return true;
			}
		}
		
		return false;
	}
	
	/** return true if "artistKey" is already rated by the current logged user **/
	public boolean getIsArtistRated(){
		
		String musicId = getArtistKey();
		String ratingResult = searchRating(RATING_TYPE_ARTIST, musicId);
		
		if (ratingResult != null){
			List<Element> ratingElement = removeExistDBElement(ratingResult);
			if (ratingElement!=null){
				if (ratingElement.get(0).elements()!=null && ratingElement.get(0).elements().size()>0)
					return true;
			}
		}
		
		return false;
	}
	
	/** return true if "trackKey" is already rated by the current logged user **/
	public boolean getIsTrackRated(){
		
		String musicId = getTrackKey();
		String ratingResult = searchRating(RATING_TYPE_TRACK, musicId);
		
		if (ratingResult != null){
			List<Element> ratingElement = removeExistDBElement(ratingResult);
			if (ratingElement!=null){
				if (ratingElement.get(0).elements()!=null && ratingElement.get(0).elements().size()>0)
					return true;
			}
		}
		
		return false;
	}

	/** do nothing **/
	public void setIsAlbumRated(boolean value){};
	
	/** Search a Rating for the current logged Usre and the music ID **/
	private String searchRating(String type, String musicId) {
		
		String userid = loginManagerBean.getLoginId();

		String query = queryBegin() + searchRateQuery(userid, type, musicId) + queryEnd();
		String result = executeQuery(query,"Ratings.xml");
		
		logger.info("Result of search rating is " + result);
		
		if (!("fail".equalsIgnoreCase(result)))
			return result;

		return null;
	}
	
	/** insert comment command **/
	private String getInsertRatingCommand(String ratingId, String type, String musicId, String date, 
			String rating, String userId){
		return "<xu:append select=\"/ratings\">" + 
				"<xu:element name=\"rating\">" +
				"<id>" + ratingId + "</id>" +
				"<type>" + type + "</type>" +
				"<music_id>" + musicId + "</music_id>" +
				"<value>" + rating + "</value>" +
				"<user_id>" + userId + "</user_id>" +
				"<date>" + date + "</date>" +
				"</xu:element></xu:append>";
	}
	
	/** album id query **/
	private String searchRateQuery(String userId, String type, String musicId){
		return "element { \"ratings\" }{ " +
			"for $rating in //rating " +
			"where $rating/user_id =\"" + userId + "\" and $rating/music_id  =\"" + musicId + "\" and $rating/type  =\"" + type + "\" " + 
			"return $rating" +
			"}";
	}
	
	/** update album rating command **/
	private String getUpdateAlbumRatingQuery(String albumId, String rating) {
		return "for $album in //album[@id='" + albumId + "'] " +
				" let $newRating :=  fn:number($album/rating) + " + rating +
				" return " +
				" update replace $album/rating with " + 
				" element { \"rating\" }{ $newRating }";
	}
	
	/** update album rating-count command **/
	private String getUpdateAlbumRatingCountQuery(String albumId) {
		return "for $album in //album[@id='" + albumId + "'] " +
				" let $newRating :=  fn:number($album/rating-count) + 1" +
				" return " +
				" update replace $album/rating-count with " + 
				" element { \"rating-count\" }{ $newRating }";
	}
	
	/** update artist rating command **/
	private String getUpdateArtistRatingQuery(String artistId, String rating) {
		return "for $artist in //artist[@id='" + artistId + "'] " +
				" let $newRating :=  fn:number($artist/rating) + " + rating +
				" return " +
				" update replace $artist/rating with " + 
				" element { \"rating\" }{ $newRating }";
	}
	
	/** update artist rating-count command **/
	private String getUpdateArtistRatingCountQuery(String artistId) {
		return "for $artist in //artist[@id='" + artistId + "'] " +
				" let $newRating :=  fn:number($artist/rating-count) + 1" +
				" return " +
				" update replace $artist/rating-count with " + 
				" element { \"rating-count\" }{ $newRating }";
	}
	
	/** update track rating command **/
	private String getUpdateTrackRatingQuery(String trackId, String rating) {
		return "for $track in //track[@id='" + trackId + "'] " +
				" let $newRating :=  fn:number($track/rating) + " + rating +
				" return " +
				" update replace $track/rating with " + 
				" element { \"rating\" }{ $newRating }";
	}
	
	/** update track rating-count command **/
	private String getUpdateTrackRatingCountQuery(String trackId) {
		return "for $track in //track[@id='" + trackId + "'] " +
				" let $newRating :=  fn:number($track/rating-count) + 1" +
				" return " +
				" update replace $track/rating-count with " + 
				" element { \"rating-count\" }{ $newRating }";
	}
}
